package edu.mse.camel.client.uml.collab.eventhandler;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.change.ChangeDescription;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.swt.widgets.Display;

import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;

import edu.mse.camel.client.core.events.model.ModelUpdateEvent2;
import edu.mse.camel.client.eventbus.pubsub.ISubscriber;
import edu.mse.camel.client.events.IEvent;

import edu.mse.camel.client.log.Debug;
import edu.mse.camel.client.uml.collab.CollabPlugin;

public class ModelUpdateEventHandler implements ISubscriber {

	private ModelManager targetModelManager;
	private Diagram target;
	
	public ModelUpdateEventHandler(Diagram target, ModelManager modelManager) {
		this.targetModelManager = modelManager;
		this.target = target;
	}
	
	private void beginCacheNotificationForAll(CamelNotificationRecorder recorder) {
		// the resourseSet itself is also a notifier
		ResourceSet root = targetModelManager.getResourceSet();
		recorder.install(root);
		for(Resource res : root.getResources()) {
			TreeIterator<EObject> tree = 
				EcoreUtil.getAllContents(Collections.singleton(res));
			while (tree.hasNext()) {
				Object obj = tree.next();
				if(obj instanceof Notifier) {
					Notifier notifier = (Notifier) obj;
					recorder.install(notifier);
				}
			}
		}
	}
		
	private void setDelivery(boolean deliver) {
		// the resourseSet itself is also a notifier
		ResourceSet root = targetModelManager.getResourceSet();
		root.eSetDeliver(deliver);
		for(Resource res : root.getResources()) {
			TreeIterator<EObject> tree = 
				EcoreUtil.getAllContents(Collections.singleton(res));
			while (tree.hasNext()) {
				Object obj = tree.next();
				if(obj instanceof Notifier) {
					Notifier notifier = (Notifier) obj;
					notifier.eSetDeliver(deliver);
				}
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void notifyEvent(final IEvent e) {
		if (e instanceof ModelUpdateEvent2) {
			final ModelUpdateEvent2 me2 = (ModelUpdateEvent2) e;
			ByteArrayInputStream bais = new ByteArrayInputStream(me2.getByteArray());
			
			try {
				ResourceSet rSet = targetModelManager.getResourceSet();
				Resource res = rSet.createResource(rSet.getResources().get(0).getURI());
				
				Map option = new HashMap();
				option.put(XMLResource.OPTION_PROCESS_DANGLING_HREF,
						XMLResource.OPTION_PROCESS_DANGLING_HREF_THROW);
				res.load(bais, option);
				
				final ChangeDescription cd = (ChangeDescription) res.getContents().get(0);
				
				Display.getDefault().syncExec(new Runnable() {
					
					@SuppressWarnings("unchecked")
					@Override
					public void run() {
						try {
							CamelNotificationRecorder recorder = new CamelNotificationRecorder();
							//setDelivery(false);
							Debug.log_info(CollabPlugin.PLUGIN_ID, "Attaching notification recorder");
							beginCacheNotificationForAll(recorder);
							Debug.log_info(CollabPlugin.PLUGIN_ID, "Applying the change");
							cd.apply();
							Debug.log_info(CollabPlugin.PLUGIN_ID, "Applying notifications");
							recorder.applyAndUninstallForAll();
							Debug.log_info(CollabPlugin.PLUGIN_ID, "Change done");
							//setDelivery(true);
						} catch (Exception e) {
							Debug.log_warning(CollabPlugin.PLUGIN_ID, "Failed to apply on target");
							e.printStackTrace();
						}
					}
					
				});
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
		}
	}

}
